home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkBind.c < prev    next >
C/C++ Source or Header  |  1995-06-04  |  71KB  |  2,461 lines

  1. /* 
  2.  * tkBind.c --
  3.  *
  4.  *    This file provides procedures that associate Tcl commands
  5.  *    with X events or sequences of X events.
  6.  *
  7.  * Copyright (c) 1989-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  */
  13.  
  14. static char sccsid[] = "@(#) tkBind.c 1.98 95/06/04 17:37:03";
  15.  
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18.  
  19. /*
  20.  * The structure below represents a binding table.  A binding table
  21.  * represents a domain in which event bindings may occur.  It includes
  22.  * a space of objects relative to which events occur (usually windows,
  23.  * but not always), a history of recent events in the domain, and
  24.  * a set of mappings that associate particular Tcl commands with sequences
  25.  * of events in the domain.  Multiple binding tables may exist at once,
  26.  * either because there are multiple applications open, or because there
  27.  * are multiple domains within an application with separate event
  28.  * bindings for each (for example, each canvas widget has a separate
  29.  * binding table for associating events with the items in the canvas).
  30.  *
  31.  * Note: it is probably a bad idea to reduce EVENT_BUFFER_SIZE much
  32.  * below 30.  To see this, consider a triple mouse button click while
  33.  * the Shift key is down (and auto-repeating).  There may be as many
  34.  * as 3 auto-repeat events after each mouse button press or release
  35.  * (see the first large comment block within Tk_BindEvent for more on
  36.  * this), for a total of 20 events to cover the three button presses
  37.  * and two intervening releases.  If you reduce EVENT_BUFFER_SIZE too
  38.  * much, shift multi-clicks will be lost.
  39.  * 
  40.  */
  41.  
  42. #define EVENT_BUFFER_SIZE 30
  43. typedef struct BindingTable {
  44.     XEvent eventRing[EVENT_BUFFER_SIZE];/* Circular queue of recent events
  45.                      * (higher indices are for more recent
  46.                      * events). */
  47.     int detailRing[EVENT_BUFFER_SIZE];    /* "Detail" information (keySym or
  48.                      * button or 0) for each entry in
  49.                      * eventRing. */
  50.     int curEvent;            /* Index in eventRing of most recent
  51.                      * event.  Newer events have higher
  52.                      * indices. */
  53.     Tcl_HashTable patternTable;        /* Used to map from an event to a list
  54.                      * of patterns that may match that
  55.                      * event.  Keys are PatternTableKey
  56.                      * structs, values are (PatSeq *). */
  57.     Tcl_HashTable objectTable;        /* Used to map from an object to a list
  58.                      * of patterns associated with that
  59.                      * object.  Keys are ClientData,
  60.                      * values are (PatSeq *). */
  61.     Tcl_Interp *interp;            /* Interpreter in which commands are
  62.                      * executed. */
  63. } BindingTable;
  64.  
  65. /*
  66.  * Structures of the following form are used as keys in the patternTable
  67.  * for a binding table:
  68.  */
  69.  
  70. typedef struct PatternTableKey {
  71.     ClientData object;        /* Identifies object (or class of objects)
  72.                  * relative to which event occurred.  For
  73.                  * example, in the widget binding table for
  74.                  * an application this is the path name of
  75.                  * a widget, or a widget class, or "all". */
  76.     int type;            /* Type of event (from X). */
  77.     int detail;            /* Additional information, such as
  78.                  * keysym or button, or 0 if nothing
  79.                  * additional.*/
  80. } PatternTableKey;
  81.  
  82. /*
  83.  * The following structure defines a pattern, which is matched
  84.  * against X events as part of the process of converting X events
  85.  * into Tcl commands.
  86.  */
  87.  
  88. typedef struct Pattern {
  89.     int eventType;        /* Type of X event, e.g. ButtonPress. */
  90.     int needMods;        /* Mask of modifiers that must be
  91.                  * present (0 means no modifiers are
  92.                  * required). */
  93.     int detail;            /* Additional information that must
  94.                  * match event.  Normally this is 0,
  95.                  * meaning no additional information
  96.                  * must match.  For KeyPress and
  97.                  * KeyRelease events, a keySym may
  98.                  * be specified to select a
  99.                  * particular keystroke (0 means any
  100.                  * keystrokes).  For button events,
  101.                  * specifies a particular button (0
  102.                  * means any buttons are OK). */
  103. } Pattern;
  104.  
  105. /*
  106.  * The structure below defines a pattern sequence, which consists
  107.  * of one or more patterns.  In order to trigger, a pattern
  108.  * sequence must match the most recent X events (first pattern
  109.  * to most recent event, next pattern to next event, and so on).
  110.  */
  111.  
  112. typedef struct PatSeq {
  113.     int numPats;        /* Number of patterns in sequence
  114.                  * (usually 1). */
  115.     char *command;        /* Command to invoke when this
  116.                  * pattern sequence matches (malloc-ed). */
  117.     int flags;            /* Miscellaneous flag values;  see
  118.                  * below for definitions. */
  119.     struct PatSeq *nextSeqPtr;
  120.                 /* Next in list of all pattern
  121.                  * sequences that have the same
  122.                  * initial pattern.  NULL means
  123.                  * end of list. */
  124.     Tcl_HashEntry *hPtr;    /* Pointer to hash table entry for
  125.                  * the initial pattern.  This is the
  126.                  * head of the list of which nextSeqPtr
  127.                  * forms a part. */
  128.     ClientData object;        /* Identifies object with which event is
  129.                  * associated (e.g. window). */
  130.     struct PatSeq *nextObjPtr;
  131.                 /* Next in list of all pattern
  132.                  * sequences for the same object
  133.                  * (NULL for end of list).  Needed to
  134.                  * implement Tk_DeleteAllBindings. */
  135.     Pattern pats[1];        /* Array of "numPats" patterns.  Only
  136.                  * one element is declared here but
  137.                  * in actuality enough space will be
  138.                  * allocated for "numPats" patterns.
  139.                  * To match, pats[0] must match event
  140.                  * n, pats[1] must match event n-1,
  141.                  * etc. */
  142. } PatSeq;
  143.  
  144. /*
  145.  * Flag values for PatSeq structures:
  146.  *
  147.  * PAT_NEARBY        1 means that all of the events matching
  148.  *            this sequence must occur with nearby X
  149.  *            and Y mouse coordinates and close in time.
  150.  *            This is typically used to restrict multiple
  151.  *            button presses.
  152.  */
  153.  
  154. #define PAT_NEARBY        1
  155.  
  156. /*
  157.  * Constants that define how close together two events must be
  158.  * in milliseconds or pixels to meet the PAT_NEARBY constraint:
  159.  */
  160.  
  161. #define NEARBY_PIXELS        5
  162. #define NEARBY_MS        500
  163.  
  164. /*
  165.  * In X11R4 and earlier versions, XStringToKeysym is ridiculously
  166.  * slow.  The data structure and hash table below, along with the
  167.  * code that uses them, implement a fast mapping from strings to
  168.  * keysyms.  In X11R5 and later releases XStringToKeysym is plenty
  169.  * fast so this stuff isn't needed.  The #define REDO_KEYSYM_LOOKUP
  170.  * is normally undefined, so that XStringToKeysym gets used.  It
  171.  * can be set in the Makefile to enable the use of the hash table
  172.  * below.
  173.  */
  174.  
  175. #ifdef REDO_KEYSYM_LOOKUP
  176. typedef struct {
  177.     char *name;                /* Name of keysym. */
  178.     KeySym value;            /* Numeric identifier for keysym. */
  179. } KeySymInfo;
  180. static KeySymInfo keyArray[] = {
  181. #ifndef lint
  182. #include "ks_names.h"
  183. #endif
  184.     {(char *) NULL, 0}
  185. };
  186. static Tcl_HashTable keySymTable;    /* Hashed form of above structure. */
  187. #endif /* REDO_KEYSYM_LOOKUP */
  188.  
  189. static int initialized = 0;
  190.  
  191. /*
  192.  * A hash table is kept to map from the string names of event
  193.  * modifiers to information about those modifiers.  The structure
  194.  * for storing this information, and the hash table built at
  195.  * initialization time, are defined below.
  196.  */
  197.  
  198. typedef struct {
  199.     char *name;            /* Name of modifier. */
  200.     int mask;            /* Button/modifier mask value,                             * such as Button1Mask. */
  201.     int flags;            /* Various flags;  see below for
  202.                  * definitions. */
  203. } ModInfo;
  204.  
  205. /*
  206.  * Flags for ModInfo structures:
  207.  *
  208.  * DOUBLE -        Non-zero means duplicate this event,
  209.  *            e.g. for double-clicks.
  210.  * TRIPLE -        Non-zero means triplicate this event,
  211.  *            e.g. for triple-clicks.
  212.  */
  213.  
  214. #define DOUBLE        1
  215. #define TRIPLE        2
  216.  
  217. /*
  218.  * The following special modifier mask bits are defined, to indicate
  219.  * logical modifiers such as Meta and Alt that may float among the
  220.  * actual modifier bits.
  221.  */
  222.  
  223. #define META_MASK    (AnyModifier<<1)
  224. #define ALT_MASK    (AnyModifier<<2)
  225.  
  226. static ModInfo modArray[] = {
  227.     {"Control",        ControlMask,    0},
  228.     {"Shift",        ShiftMask,    0},
  229.     {"Lock",        LockMask,    0},
  230.     {"Meta",        META_MASK,    0},
  231.     {"M",        META_MASK,    0},
  232.     {"Alt",        ALT_MASK,    0},
  233.     {"B1",        Button1Mask,    0},
  234.     {"Button1",        Button1Mask,    0},
  235.     {"B2",        Button2Mask,    0},
  236.     {"Button2",        Button2Mask,    0},
  237.     {"B3",        Button3Mask,    0},
  238.     {"Button3",        Button3Mask,    0},
  239.     {"B4",        Button4Mask,    0},
  240.     {"Button4",        Button4Mask,    0},
  241.     {"B5",        Button5Mask,    0},
  242.     {"Button5",        Button5Mask,    0},
  243.     {"Mod1",        Mod1Mask,    0},
  244.     {"M1",        Mod1Mask,    0},
  245.     {"Mod2",        Mod2Mask,    0},
  246.     {"M2",        Mod2Mask,    0},
  247.     {"Mod3",        Mod3Mask,    0},
  248.     {"M3",        Mod3Mask,    0},
  249.     {"Mod4",        Mod4Mask,    0},
  250.     {"M4",        Mod4Mask,    0},
  251.     {"Mod5",        Mod5Mask,    0},
  252.     {"M5",        Mod5Mask,    0},
  253.     {"Double",        0,        DOUBLE},
  254.     {"Triple",        0,        TRIPLE},
  255.     {"Any",        0,        0},    /* Ignored: historical relic. */
  256.     {NULL,        0,        0}
  257. };
  258. static Tcl_HashTable modTable;
  259.  
  260. /*
  261.  * This module also keeps a hash table mapping from event names
  262.  * to information about those events.  The structure, an array
  263.  * to use to initialize the hash table, and the hash table are
  264.  * all defined below.
  265.  */
  266.  
  267. typedef struct {
  268.     char *name;            /* Name of event. */
  269.     int type;            /* Event type for X, such as
  270.                  * ButtonPress. */
  271.     int eventMask;        /* Mask bits (for XSelectInput)
  272.                  * for this event type. */
  273. } EventInfo;
  274.  
  275. /*
  276.  * Note:  some of the masks below are an OR-ed combination of
  277.  * several masks.  This is necessary because X doesn't report
  278.  * up events unless you also ask for down events.  Also, X
  279.  * doesn't report button state in motion events unless you've
  280.  * asked about button events.
  281.  */
  282.  
  283. static EventInfo eventArray[] = {
  284.     {"Motion",        MotionNotify,
  285.         ButtonPressMask|PointerMotionMask},
  286.     {"Button",        ButtonPress,        ButtonPressMask},
  287.     {"ButtonPress",    ButtonPress,        ButtonPressMask},
  288.     {"ButtonRelease",    ButtonRelease,
  289.         ButtonPressMask|ButtonReleaseMask},
  290.     {"Colormap",    ColormapNotify,        ColormapChangeMask},
  291.     {"Enter",        EnterNotify,        EnterWindowMask},
  292.     {"Leave",        LeaveNotify,        LeaveWindowMask},
  293.     {"Expose",        Expose,            ExposureMask},
  294.     {"FocusIn",        FocusIn,        FocusChangeMask},
  295.     {"FocusOut",    FocusOut,        FocusChangeMask},
  296.     {"Key",        KeyPress,        KeyPressMask},
  297.     {"KeyPress",    KeyPress,        KeyPressMask},
  298.     {"KeyRelease",    KeyRelease,
  299.         KeyPressMask|KeyReleaseMask},
  300.     {"Property",    PropertyNotify,        PropertyChangeMask},
  301.     {"Circulate",    CirculateNotify,    StructureNotifyMask},
  302.     {"Configure",    ConfigureNotify,    StructureNotifyMask},
  303.     {"Destroy",        DestroyNotify,        StructureNotifyMask},
  304.     {"Gravity",        GravityNotify,        StructureNotifyMask},
  305.     {"Map",        MapNotify,        StructureNotifyMask},
  306.     {"Reparent",    ReparentNotify,        StructureNotifyMask},
  307.     {"Unmap",        UnmapNotify,        StructureNotifyMask},
  308.     {"Visibility",    VisibilityNotify,    VisibilityChangeMask},
  309.     {(char *) NULL,    0,            0}
  310. };
  311. static Tcl_HashTable eventTable;
  312.  
  313. /*
  314.  * The defines and table below are used to classify events into
  315.  * various groups.  The reason for this is that logically identical
  316.  * fields (e.g. "state") appear at different places in different
  317.  * types of events.  The classification masks can be used to figure
  318.  * out quickly where to extract information from events.
  319.  */
  320.  
  321. #define KEY_BUTTON_MOTION    0x1
  322. #define CROSSING        0x2
  323. #define FOCUS            0x4
  324. #define EXPOSE            0x8
  325. #define VISIBILITY        0x10
  326. #define CREATE            0x20
  327. #define MAP            0x40
  328. #define REPARENT        0x80
  329. #define CONFIG            0x100
  330. #define CONFIG_REQ        0x200
  331. #define RESIZE_REQ        0x400
  332. #define GRAVITY            0x800
  333. #define PROP            0x1000
  334. #define SEL_CLEAR        0x2000
  335. #define SEL_REQ            0x4000
  336. #define SEL_NOTIFY        0x8000
  337. #define COLORMAP        0x10000
  338. #define MAPPING            0x20000
  339.  
  340. static int flagArray[LASTEvent] = {
  341.    /* Not used */        0,
  342.    /* Not used */        0,
  343.    /* KeyPress */        KEY_BUTTON_MOTION,
  344.    /* KeyRelease */        KEY_BUTTON_MOTION,
  345.    /* ButtonPress */        KEY_BUTTON_MOTION,
  346.    /* ButtonRelease */        KEY_BUTTON_MOTION,
  347.    /* MotionNotify */        KEY_BUTTON_MOTION,
  348.    /* EnterNotify */        CROSSING,
  349.    /* LeaveNotify */        CROSSING,
  350.    /* FocusIn */        FOCUS,
  351.    /* FocusOut */        FOCUS,
  352.    /* KeymapNotify */        0,
  353.    /* Expose */            EXPOSE,
  354.    /* GraphicsExpose */        EXPOSE,
  355.    /* NoExpose */        0,
  356.    /* VisibilityNotify */    VISIBILITY,
  357.    /* CreateNotify */        CREATE,
  358.    /* DestroyNotify */        0,
  359.    /* UnmapNotify */        0,
  360.    /* MapNotify */        MAP,
  361.    /* MapRequest */        0,
  362.    /* ReparentNotify */        REPARENT,
  363.    /* ConfigureNotify */    CONFIG,
  364.    /* ConfigureRequest */    CONFIG_REQ,
  365.    /* GravityNotify */        0,
  366.    /* ResizeRequest */        RESIZE_REQ,
  367.    /* CirculateNotify */    0,
  368.    /* CirculateRequest */    0,
  369.    /* PropertyNotify */        PROP,
  370.    /* SelectionClear */        SEL_CLEAR,
  371.    /* SelectionRequest */    SEL_REQ,
  372.    /* SelectionNotify */    SEL_NOTIFY,
  373.    /* ColormapNotify */        COLORMAP,
  374.    /* ClientMessage */        0,
  375.    /* MappingNotify */        MAPPING
  376. };
  377.  
  378. /*
  379.  * Prototypes for local procedures defined in this file:
  380.  */
  381.  
  382. static void        ChangeScreen _ANSI_ARGS_((Tcl_Interp *interp,
  383.                 char *dispName, int screenIndex));
  384. static void        ExpandPercents _ANSI_ARGS_((TkWindow *winPtr,
  385.                 char *before, XEvent *eventPtr, KeySym keySym,
  386.                 Tcl_DString *dsPtr));
  387. static PatSeq *        FindSequence _ANSI_ARGS_((Tcl_Interp *interp,
  388.                 BindingTable *bindPtr, ClientData object,
  389.                 char *eventString, int create,
  390.                 unsigned long *maskPtr));
  391. static char *        GetField _ANSI_ARGS_((char *p, char *copy, int size));
  392. static KeySym        GetKeySym _ANSI_ARGS_((TkDisplay *dispPtr,
  393.                 XEvent *eventPtr));
  394. static void        InitKeymapInfo _ANSI_ARGS_((TkDisplay *dispPtr));
  395. static PatSeq *        MatchPatterns _ANSI_ARGS_((TkDisplay *dispPtr,
  396.                 BindingTable *bindPtr, PatSeq *psPtr));
  397. static KeySym        StringToKeysym _ANSI_ARGS_((char *name));
  398.  
  399. /*
  400.  *--------------------------------------------------------------
  401.  *
  402.  * Tk_CreateBindingTable --
  403.  *
  404.  *    Set up a new domain in which event bindings may be created.
  405.  *
  406.  * Results:
  407.  *    The return value is a token for the new table, which must
  408.  *    be passed to procedures like Tk_CreatBinding.
  409.  *
  410.  * Side effects:
  411.  *    Memory is allocated for the new table.
  412.  *
  413.  *--------------------------------------------------------------
  414.  */
  415.  
  416. Tk_BindingTable
  417. Tk_CreateBindingTable(interp)
  418.     Tcl_Interp *interp;        /* Interpreter to associate with the binding
  419.                  * table:  commands are executed in this
  420.                  * interpreter. */
  421. {
  422.     register BindingTable *bindPtr;
  423.     int i;
  424.  
  425.     /*
  426.      * If this is the first time a binding table has been created,
  427.      * initialize the global data structures.
  428.      */
  429.  
  430.     if (!initialized) {
  431.     register Tcl_HashEntry *hPtr;
  432.     register ModInfo *modPtr;
  433.     register EventInfo *eiPtr;
  434.     int dummy;
  435.  
  436. #ifdef REDO_KEYSYM_LOOKUP
  437.     register KeySymInfo *kPtr;
  438.  
  439.     Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS);
  440.     for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
  441.         hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy);
  442.         Tcl_SetHashValue(hPtr, kPtr->value);
  443.     }
  444. #endif /* REDO_KEYSYM_LOOKUP */
  445.  
  446.     initialized = 1;
  447.     
  448.     Tcl_InitHashTable(&modTable, TCL_STRING_KEYS);
  449.     for (modPtr = modArray; modPtr->name != NULL; modPtr++) {
  450.         hPtr = Tcl_CreateHashEntry(&modTable, modPtr->name, &dummy);
  451.         Tcl_SetHashValue(hPtr, modPtr);
  452.     }
  453.     
  454.     Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS);
  455.     for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
  456.         hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy);
  457.         Tcl_SetHashValue(hPtr, eiPtr);
  458.     }
  459.     }
  460.  
  461.     /*
  462.      * Create and initialize a new binding table.
  463.      */
  464.  
  465.     bindPtr = (BindingTable *) ckalloc(sizeof(BindingTable));
  466.     for (i = 0; i < EVENT_BUFFER_SIZE; i++) {
  467.     bindPtr->eventRing[i].type = -1;
  468.     }
  469.     bindPtr->curEvent = 0;
  470.     Tcl_InitHashTable(&bindPtr->patternTable,
  471.         sizeof(PatternTableKey)/sizeof(int));
  472.     Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS);
  473.     bindPtr->interp = interp;
  474.     return (Tk_BindingTable) bindPtr;
  475. }
  476.  
  477. /*
  478.  *--------------------------------------------------------------
  479.  *
  480.  * Tk_DeleteBindingTable --
  481.  *
  482.  *    Destroy a binding table and free up all its memory.
  483.  *    The caller should not use bindingTable again after
  484.  *    this procedure returns.
  485.  *
  486.  * Results:
  487.  *    None.
  488.  *
  489.  * Side effects:
  490.  *    Memory is freed.
  491.  *
  492.  *--------------------------------------------------------------
  493.  */
  494.  
  495. void
  496. Tk_DeleteBindingTable(bindingTable)
  497.     Tk_BindingTable bindingTable;    /* Token for the binding table to
  498.                      * destroy. */
  499. {
  500.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  501.     PatSeq *psPtr, *nextPtr;
  502.     Tcl_HashEntry *hPtr;
  503.     Tcl_HashSearch search;
  504.  
  505.     /*
  506.      * Find and delete all of the patterns associated with the binding
  507.      * table.
  508.      */
  509.  
  510.     for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search);
  511.         hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  512.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  513.         psPtr != NULL; psPtr = nextPtr) {
  514.         nextPtr = psPtr->nextSeqPtr;
  515.         ckfree((char *) psPtr->command);
  516.         ckfree((char *) psPtr);
  517.     }
  518.     }
  519.  
  520.     /*
  521.      * Clean up the rest of the information associated with the
  522.      * binding table.
  523.      */
  524.  
  525.     Tcl_DeleteHashTable(&bindPtr->patternTable);
  526.     Tcl_DeleteHashTable(&bindPtr->objectTable);
  527.     ckfree((char *) bindPtr);
  528. }
  529.  
  530. /*
  531.  *--------------------------------------------------------------
  532.  *
  533.  * Tk_CreateBinding --
  534.  *
  535.  *    Add a binding to a binding table, so that future calls to
  536.  *    Tk_BindEvent may execute the command in the binding.
  537.  *
  538.  * Results:
  539.  *    The return value is 0 if an error occurred while setting
  540.  *    up the binding.  In this case, an error message will be
  541.  *    left in interp->result.  If all went well then the return
  542.  *    value is a mask of the event types that must be made
  543.  *    available to Tk_BindEvent in order to properly detect when
  544.  *    this binding triggers.  This value can be used to determine
  545.  *    what events to select for in a window, for example.
  546.  *
  547.  * Side effects:
  548.  *    The new binding may cause future calls to Tk_BindEvent to
  549.  *    behave differently than they did previously.
  550.  *
  551.  *--------------------------------------------------------------
  552.  */
  553.  
  554. unsigned long
  555. Tk_CreateBinding(interp, bindingTable, object, eventString, command, append)
  556.     Tcl_Interp *interp;            /* Used for error reporting. */
  557.     Tk_BindingTable bindingTable;    /* Table in which to create binding. */
  558.     ClientData object;            /* Token for object with which binding
  559.                      * is associated. */
  560.     char *eventString;            /* String describing event sequence
  561.                      * that triggers binding. */
  562.     char *command;            /* Contains Tcl command to execute
  563.                      * when binding triggers. */
  564.     int append;                /* 0 means replace any existing
  565.                      * binding for eventString;  1 means
  566.                      * append to that binding. */
  567. {
  568.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  569.     register PatSeq *psPtr;
  570.     unsigned long eventMask;
  571.  
  572.     psPtr = FindSequence(interp, bindPtr, object, eventString, 1, &eventMask);
  573.     if (psPtr == NULL) {
  574.     return 0;
  575.     }
  576.     if (append && (psPtr->command != NULL)) {
  577.     int length;
  578.     char *new;
  579.  
  580.     length = strlen(psPtr->command) + strlen(command) + 2;
  581.     new = (char *) ckalloc((unsigned) length);
  582.     sprintf(new, "%s\n%s", psPtr->command, command);
  583.     ckfree((char *) psPtr->command);
  584.     psPtr->command = new;
  585.     } else {
  586.     if (psPtr->command != NULL) {
  587.         ckfree((char *) psPtr->command);
  588.     }
  589.     psPtr->command = (char *) ckalloc((unsigned) (strlen(command) + 1));
  590.     strcpy(psPtr->command, command);
  591.     }
  592.     return eventMask;
  593. }
  594.  
  595. /*
  596.  *--------------------------------------------------------------
  597.  *
  598.  * Tk_DeleteBinding --
  599.  *
  600.  *    Remove an event binding from a binding table.
  601.  *
  602.  * Results:
  603.  *    The result is a standard Tcl return value.  If an error
  604.  *    occurs then interp->result will contain an error message.
  605.  *
  606.  * Side effects:
  607.  *    The binding given by object and eventString is removed
  608.  *    from bindingTable.
  609.  *
  610.  *--------------------------------------------------------------
  611.  */
  612.  
  613. int
  614. Tk_DeleteBinding(interp, bindingTable, object, eventString)
  615.     Tcl_Interp *interp;            /* Used for error reporting. */
  616.     Tk_BindingTable bindingTable;    /* Table in which to delete binding. */
  617.     ClientData object;            /* Token for object with which binding
  618.                      * is associated. */
  619.     char *eventString;            /* String describing event sequence
  620.                      * that triggers binding. */
  621. {
  622.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  623.     register PatSeq *psPtr, *prevPtr;
  624.     unsigned long eventMask;
  625.     Tcl_HashEntry *hPtr;
  626.  
  627.     psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask);
  628.     if (psPtr == NULL) {
  629.     Tcl_ResetResult(interp);
  630.     return TCL_OK;
  631.     }
  632.  
  633.     /*
  634.      * Unlink the binding from the list for its object, then from the
  635.      * list for its pattern.
  636.      */
  637.  
  638.     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
  639.     if (hPtr == NULL) {
  640.     panic("Tk_DeleteBinding couldn't find object table entry");
  641.     }
  642.     prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  643.     if (prevPtr == psPtr) {
  644.     Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);
  645.     } else {
  646.     for ( ; ; prevPtr = prevPtr->nextObjPtr) {
  647.         if (prevPtr == NULL) {
  648.         panic("Tk_DeleteBinding couldn't find on object list");
  649.         }
  650.         if (prevPtr->nextObjPtr == psPtr) {
  651.         prevPtr->nextObjPtr = psPtr->nextObjPtr;
  652.         break;
  653.         }
  654.     }
  655.     }
  656.     prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
  657.     if (prevPtr == psPtr) {
  658.     if (psPtr->nextSeqPtr == NULL) {
  659.         Tcl_DeleteHashEntry(psPtr->hPtr);
  660.     } else {
  661.         Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
  662.     }
  663.     } else {
  664.     for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
  665.         if (prevPtr == NULL) {
  666.         panic("Tk_DeleteBinding couldn't find on hash chain");
  667.         }
  668.         if (prevPtr->nextSeqPtr == psPtr) {
  669.         prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
  670.         break;
  671.         }
  672.     }
  673.     }
  674.     ckfree((char *) psPtr->command);
  675.     ckfree((char *) psPtr);
  676.     return TCL_OK;
  677. }
  678.  
  679. /*
  680.  *--------------------------------------------------------------
  681.  *
  682.  * Tk_GetBinding --
  683.  *
  684.  *    Return the command associated with a given event string.
  685.  *
  686.  * Results:
  687.  *    The return value is a pointer to the command string
  688.  *    associated with eventString for object in the domain
  689.  *    given by bindingTable.  If there is no binding for
  690.  *    eventString, or if eventString is improperly formed,
  691.  *    then NULL is returned and an error message is left in
  692.  *    interp->result.  The return value is semi-static:  it
  693.  *    will persist until the binding is changed or deleted.
  694.  *
  695.  * Side effects:
  696.  *    None.
  697.  *
  698.  *--------------------------------------------------------------
  699.  */
  700.  
  701. char *
  702. Tk_GetBinding(interp, bindingTable, object, eventString)
  703.     Tcl_Interp *interp;            /* Interpreter for error reporting. */
  704.     Tk_BindingTable bindingTable;    /* Table in which to look for
  705.                      * binding. */
  706.     ClientData object;            /* Token for object with which binding
  707.                      * is associated. */
  708.     char *eventString;            /* String describing event sequence
  709.                      * that triggers binding. */
  710. {
  711.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  712.     register PatSeq *psPtr;
  713.     unsigned long eventMask;
  714.  
  715.     psPtr = FindSequence(interp, bindPtr, object, eventString, 0, &eventMask);
  716.     if (psPtr == NULL) {
  717.     return NULL;
  718.     }
  719.     return psPtr->command;
  720. }
  721.  
  722. /*
  723.  *--------------------------------------------------------------
  724.  *
  725.  * Tk_GetAllBindings --
  726.  *
  727.  *    Return a list of event strings for all the bindings
  728.  *    associated with a given object.
  729.  *
  730.  * Results:
  731.  *    There is no return value.  Interp->result is modified to
  732.  *    hold a Tcl list with one entry for each binding associated
  733.  *    with object in bindingTable.  Each entry in the list
  734.  *    contains the event string associated with one binding.
  735.  *
  736.  * Side effects:
  737.  *    None.
  738.  *
  739.  *--------------------------------------------------------------
  740.  */
  741.  
  742. void
  743. Tk_GetAllBindings(interp, bindingTable, object)
  744.     Tcl_Interp *interp;            /* Interpreter returning result or
  745.                      * error. */
  746.     Tk_BindingTable bindingTable;    /* Table in which to look for
  747.                      * bindings. */
  748.     ClientData object;            /* Token for object. */
  749.  
  750. {
  751.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  752.     register PatSeq *psPtr;
  753.     register Pattern *patPtr;
  754.     Tcl_HashEntry *hPtr;
  755.     Tcl_DString ds;
  756.     char c, buffer[10];
  757.     int patsLeft, needMods;
  758.     register ModInfo *modPtr;
  759.     register EventInfo *eiPtr;
  760.  
  761.     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
  762.     if (hPtr == NULL) {
  763.     return;
  764.     }
  765.     Tcl_DStringInit(&ds);
  766.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
  767.         psPtr = psPtr->nextObjPtr) {
  768.     Tcl_DStringSetLength(&ds, 0);
  769.  
  770.     /*
  771.      * For each binding, output information about each of the
  772.      * patterns in its sequence.  The order of the patterns in
  773.      * the sequence is backwards from the order in which they
  774.      * must be output.
  775.      */
  776.  
  777.     for (patsLeft = psPtr->numPats,
  778.         patPtr = &psPtr->pats[psPtr->numPats - 1];
  779.         patsLeft > 0; patsLeft--, patPtr--) {
  780.  
  781.         /*
  782.          * Check for simple case of an ASCII character.
  783.          */
  784.  
  785.         if ((patPtr->eventType == KeyPress)
  786.             && (patPtr->needMods == 0)
  787.             && (patPtr->detail < 128)
  788.             && isprint(UCHAR(patPtr->detail))
  789.             && (patPtr->detail != '<')
  790.             && (patPtr->detail != ' ')) {
  791.  
  792.         c = patPtr->detail;
  793.         Tcl_DStringAppend(&ds, &c, 1);
  794.         continue;
  795.         }
  796.  
  797.         /*
  798.          * It's a more general event specification.  First check
  799.          * for "Double" or "Triple", then modifiers, then event type,
  800.          * then keysym or button detail.
  801.          */
  802.  
  803.         Tcl_DStringAppend(&ds, "<", 1);
  804.         if ((patsLeft > 1) && (memcmp((char *) patPtr,
  805.             (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
  806.         patsLeft--;
  807.         patPtr--;
  808.         if ((patsLeft > 1) && (memcmp((char *) patPtr,
  809.             (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
  810.             patsLeft--;
  811.             patPtr--;
  812.             Tcl_DStringAppend(&ds, "Triple-", 7);
  813.         } else {
  814.             Tcl_DStringAppend(&ds, "Double-", 7);
  815.         }
  816.         }
  817.  
  818.         for (needMods = patPtr->needMods, modPtr = modArray;
  819.             needMods != 0; modPtr++) {
  820.         if (modPtr->mask & needMods) {
  821.             needMods &= ~modPtr->mask;
  822.             Tcl_DStringAppend(&ds, modPtr->name, -1);
  823.             Tcl_DStringAppend(&ds, "-", 1);
  824.         }
  825.         }
  826.  
  827.         for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
  828.         if (eiPtr->type == patPtr->eventType) {
  829.             Tcl_DStringAppend(&ds, eiPtr->name, -1);
  830.             if (patPtr->detail != 0) {
  831.             Tcl_DStringAppend(&ds, "-", 1);
  832.             }
  833.             break;
  834.         }
  835.         }
  836.  
  837.         if (patPtr->detail != 0) {
  838.         if ((patPtr->eventType == KeyPress)
  839.             || (patPtr->eventType == KeyRelease)) {
  840.             char *string;
  841.  
  842.             string = XKeysymToString((KeySym) patPtr->detail);
  843.             if (string != NULL) {
  844.             Tcl_DStringAppend(&ds, string, -1);
  845.             }
  846.         } else {
  847.             sprintf(buffer, "%d", patPtr->detail);
  848.             Tcl_DStringAppend(&ds, buffer, -1);
  849.         }
  850.         }
  851.         Tcl_DStringAppend(&ds, ">", 1);
  852.     }
  853.     Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
  854.     }
  855.     Tcl_DStringFree(&ds);
  856. }
  857.  
  858. /*
  859.  *--------------------------------------------------------------
  860.  *
  861.  * Tk_DeleteAllBindings --
  862.  *
  863.  *    Remove all bindings associated with a given object in a
  864.  *    given binding table.
  865.  *
  866.  * Results:
  867.  *    All bindings associated with object are removed from
  868.  *    bindingTable.
  869.  *
  870.  * Side effects:
  871.  *    None.
  872.  *
  873.  *--------------------------------------------------------------
  874.  */
  875.  
  876. void
  877. Tk_DeleteAllBindings(bindingTable, object)
  878.     Tk_BindingTable bindingTable;    /* Table in which to delete
  879.                      * bindings. */
  880.     ClientData object;            /* Token for object. */
  881. {
  882.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  883.     register PatSeq *psPtr, *prevPtr;
  884.     PatSeq *nextPtr;
  885.     Tcl_HashEntry *hPtr;
  886.  
  887.     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
  888.     if (hPtr == NULL) {
  889.     return;
  890.     }
  891.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
  892.         psPtr = nextPtr) {
  893.     nextPtr  = psPtr->nextObjPtr;
  894.  
  895.     /*
  896.      * Be sure to remove each binding from its hash chain in the
  897.      * pattern table.  If this is the last pattern in the chain,
  898.      * then delete the hash entry too.
  899.      */
  900.  
  901.     prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
  902.     if (prevPtr == psPtr) {
  903.         if (psPtr->nextSeqPtr == NULL) {
  904.         Tcl_DeleteHashEntry(psPtr->hPtr);
  905.         } else {
  906.         Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
  907.         }
  908.     } else {
  909.         for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
  910.         if (prevPtr == NULL) {
  911.             panic("Tk_DeleteAllBindings couldn't find on hash chain");
  912.         }
  913.         if (prevPtr->nextSeqPtr == psPtr) {
  914.             prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
  915.             break;
  916.         }
  917.         }
  918.     }
  919.     ckfree((char *) psPtr->command);
  920.     ckfree((char *) psPtr);
  921.     }
  922.     Tcl_DeleteHashEntry(hPtr);
  923. }
  924.  
  925. /*
  926.  *--------------------------------------------------------------
  927.  *
  928.  * Tk_BindEvent --
  929.  *
  930.  *    This procedure is invoked to process an X event.  The
  931.  *    event is added to those recorded for the binding table.
  932.  *    Then each of the objects at *objectPtr is checked in
  933.  *    order to see if it has a binding that matches the recent
  934.  *    events.  If so, that binding is invoked and the rest of
  935.  *    objects are skipped.
  936.  *
  937.  * Results:
  938.  *    None.
  939.  *
  940.  * Side effects:
  941.  *    Depends on the command associated with the matching
  942.  *    binding.
  943.  *
  944.  *--------------------------------------------------------------
  945.  */
  946.  
  947. void
  948. Tk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr)
  949.     Tk_BindingTable bindingTable;    /* Table in which to look for
  950.                      * bindings. */
  951.     XEvent *eventPtr;            /* What actually happened. */
  952.     Tk_Window tkwin;            /* Window on display where event
  953.                      * occurred (needed in order to
  954.                      * locate display information). */
  955.     int numObjects;            /* Number of objects at *objectPtr. */
  956.     ClientData *objectPtr;        /* Array of one or more objects
  957.                      * to check for a matching binding. */
  958. {
  959.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  960.     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
  961.     TkMainInfo *mainPtr;
  962.     TkDisplay *oldDispPtr;
  963.     XEvent *ringPtr;
  964.     PatSeq *matchPtr;
  965.     PatternTableKey key;
  966.     Tcl_HashEntry *hPtr;
  967.     int detail, code, oldScreen;
  968.     Tcl_Interp *interp;
  969.     Tcl_DString scripts, savedResult;
  970.     char *p, *end;
  971.  
  972.     /*
  973.      * Ignore the event completely if it is an Enter, Leave, FocusIn,
  974.      * or FocusOut event with detail NotifyInferior.  The reason for
  975.      * ignoring these events is that we don't want transitions between
  976.      * a window and its children to visible to bindings on the parent:
  977.      * this would cause problems for mega-widgets, since the internal
  978.      * structure of a mega-widget isn't supposed to be visible to
  979.      * people watching the parent.
  980.      */
  981.  
  982.     if ((eventPtr->type == EnterNotify)  || (eventPtr->type == LeaveNotify)) {
  983.     if (eventPtr->xcrossing.detail == NotifyInferior) {
  984.         return;
  985.     }
  986.     }
  987.     if ((eventPtr->type == FocusIn)  || (eventPtr->type == FocusOut)) {
  988.     if (eventPtr->xfocus.detail == NotifyInferior) {
  989.         return;
  990.     }
  991.     }
  992.  
  993.     /*
  994.      * Add the new event to the ring of saved events for the
  995.      * binding table.  Two tricky points:
  996.      *
  997.      * 1. Combine consecutive MotionNotify events.  Do this by putting
  998.      *    the new event *on top* of the previous event.
  999.      * 2. If a modifier key is held down, it auto-repeats to generate
  1000.      *    continuous KeyPress and KeyRelease events.  These can flush
  1001.      *    the event ring so that valuable information is lost (such
  1002.      *    as repeated button clicks).  To handle this, check for the
  1003.      *    special case of a modifier KeyPress arriving when the previous
  1004.      *    two events are a KeyRelease and KeyPress of the same key.
  1005.      *    If this happens, mark the most recent event (the KeyRelease)
  1006.      *    invalid and put the new event on top of the event before that
  1007.      *    (the KeyPress).
  1008.      */
  1009.  
  1010.     if ((eventPtr->type == MotionNotify)
  1011.         && (bindPtr->eventRing[bindPtr->curEvent].type == MotionNotify)) {
  1012.     /*
  1013.      * Don't advance the ring pointer.
  1014.      */
  1015.     } else if (eventPtr->type == KeyPress) {
  1016.     int i;
  1017.     for (i = 0; ; i++) {
  1018.         if (i >= dispPtr->numModKeyCodes) {
  1019.         goto advanceRingPointer;
  1020.         }
  1021.         if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {
  1022.         break;
  1023.         }
  1024.     }
  1025.     ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1026.     if ((ringPtr->type != KeyRelease)
  1027.         || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {
  1028.         goto advanceRingPointer;
  1029.     }
  1030.     if (bindPtr->curEvent <= 0) {
  1031.         i = EVENT_BUFFER_SIZE - 1;
  1032.     } else {
  1033.         i = bindPtr->curEvent - 1;
  1034.     }
  1035.     ringPtr = &bindPtr->eventRing[i];
  1036.     if ((ringPtr->type != KeyPress)
  1037.         || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {
  1038.         goto advanceRingPointer;
  1039.     }
  1040.     bindPtr->eventRing[bindPtr->curEvent].type = -1;
  1041.     bindPtr->curEvent = i;
  1042.     } else {
  1043.     advanceRingPointer:
  1044.     bindPtr->curEvent++;
  1045.     if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) {
  1046.         bindPtr->curEvent = 0;
  1047.     }
  1048.     }
  1049.     ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1050.     memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent));
  1051.     detail = 0;
  1052.     bindPtr->detailRing[bindPtr->curEvent] = 0;
  1053.     if ((ringPtr->type == KeyPress) || (ringPtr->type == KeyRelease)) {
  1054.     detail = (int) GetKeySym(dispPtr, ringPtr);
  1055.     if (detail == NoSymbol) {
  1056.         detail = 0;
  1057.     }
  1058.     } else if ((ringPtr->type == ButtonPress)
  1059.         || (ringPtr->type == ButtonRelease)) {
  1060.     detail = ringPtr->xbutton.button;
  1061.     }
  1062.     bindPtr->detailRing[bindPtr->curEvent] = detail;
  1063.  
  1064.     /*
  1065.      * Loop over all the objects, finding the binding script for each
  1066.      * one.  Append all of the binding scripts, with %-sequences expanded,
  1067.      * to "scripts", with null characters separating the scripts for
  1068.      * each object.
  1069.      */
  1070.  
  1071.     Tcl_DStringInit(&scripts);
  1072.     for ( ; numObjects > 0; numObjects--, objectPtr++) {
  1073.  
  1074.     /*
  1075.      * Match the new event against those recorded in the
  1076.      * pattern table, saving the longest matching pattern.
  1077.      * For events with details (button and key events) first
  1078.      * look for a binding for the specific key or button.
  1079.      * If none is found, then look for a binding for all
  1080.      * keys or buttons (detail of 0).
  1081.      */
  1082.     
  1083.     matchPtr = NULL;
  1084.     key.object = *objectPtr;
  1085.     key.type = ringPtr->type;
  1086.     key.detail = detail;
  1087.     hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
  1088.     if (hPtr != NULL) {
  1089.         matchPtr = MatchPatterns(dispPtr, bindPtr,
  1090.             (PatSeq *) Tcl_GetHashValue(hPtr));
  1091.     }
  1092.     if ((detail != 0) && (matchPtr == NULL)) {
  1093.         key.detail = 0;
  1094.         hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
  1095.         if (hPtr != NULL) {
  1096.         matchPtr = MatchPatterns(dispPtr, bindPtr,
  1097.             (PatSeq *) Tcl_GetHashValue(hPtr));
  1098.         }
  1099.     }
  1100.     
  1101.     if (matchPtr != NULL) {
  1102.         ExpandPercents((TkWindow *) tkwin, matchPtr->command, eventPtr,
  1103.             (KeySym) detail, &scripts);
  1104.         Tcl_DStringAppend(&scripts, "", 1);
  1105.     }
  1106.     }
  1107.  
  1108.     /*
  1109.      * Now go back through and evaluate the script for each object,
  1110.      * in order, dealing with "break" and "continue" exceptions
  1111.      * appropriately.
  1112.      *
  1113.      * There are two tricks here:
  1114.      * 1. Bindings can be invoked from in the middle of Tcl commands,
  1115.      *    where interp->result is significant (for example, a widget
  1116.      *    might be deleted because of an error in creating it, so the
  1117.      *    result contains an error message that is eventually going to
  1118.      *    be returned by the creating command).  To preserve the result,
  1119.      *    we save it in a dynamic string.
  1120.      * 2. The binding's action can potentially delete the binding,
  1121.      *    so bindPtr may not point to anything valid once the action
  1122.      *    completes.  Thus we have to save bindPtr->interp in a
  1123.      *    local variable in order to restore the result.
  1124.      * 3. When the screen changes, must invoke a Tcl script to update
  1125.      *    Tcl level information such as tkPriv.
  1126.      */
  1127.  
  1128.     mainPtr = ((TkWindow *) tkwin)->mainPtr;
  1129.     oldDispPtr = mainPtr->curDispPtr;
  1130.     oldScreen = mainPtr->curScreenIndex;
  1131.     interp = bindPtr->interp;
  1132.     Tcl_DStringInit(&savedResult);
  1133.     Tcl_DStringGetResult(interp, &savedResult);
  1134.     p = Tcl_DStringValue(&scripts);
  1135.     end = p + Tcl_DStringLength(&scripts);
  1136.     while (p != end) {
  1137.     if ((dispPtr != mainPtr->curDispPtr)
  1138.         || (Tk_ScreenNumber(tkwin) != mainPtr->curScreenIndex)) {
  1139.         mainPtr->curDispPtr = dispPtr;
  1140.         mainPtr->curScreenIndex = Tk_ScreenNumber(tkwin);
  1141.         ChangeScreen(interp, dispPtr->name, mainPtr->curScreenIndex);
  1142.     }
  1143.     mainPtr->bindingDepth += 1;
  1144.     Tcl_AllowExceptions(interp);
  1145.     code = Tcl_GlobalEval(interp, p);
  1146.     mainPtr->bindingDepth -= 1;
  1147.     if (code != TCL_OK) {
  1148.         if (code == TCL_CONTINUE) {
  1149.         /*
  1150.          * Do nothing:  just go on to the next script.
  1151.          */
  1152.         } else if (code == TCL_BREAK) {
  1153.         break;
  1154.         } else {
  1155.         Tcl_AddErrorInfo(interp, "\n    (command bound to event)");
  1156.         Tk_BackgroundError(interp);
  1157.         break;
  1158.         }
  1159.     }
  1160.  
  1161.     /*
  1162.      * Skip over the current script and its terminating null character.
  1163.      */
  1164.  
  1165.     while (*p != 0) {
  1166.         p++;
  1167.     }
  1168.     p++;
  1169.     }
  1170.     if ((mainPtr->bindingDepth != 0) && ((oldDispPtr != mainPtr->curDispPtr)
  1171.         || (oldScreen != mainPtr->curScreenIndex))) {
  1172.     /*
  1173.      * Some other binding script is currently executing, but its
  1174.      * screen is no longer current.  Change the current display
  1175.      * back again.
  1176.      */
  1177.  
  1178.     mainPtr->curDispPtr = oldDispPtr;
  1179.     mainPtr->curScreenIndex = oldScreen;
  1180.     ChangeScreen(interp, oldDispPtr->name, oldScreen);
  1181.     }
  1182.     Tcl_DStringResult(interp, &savedResult);
  1183.     Tcl_DStringFree(&scripts);
  1184. }
  1185.  
  1186. /*
  1187.  *----------------------------------------------------------------------
  1188.  *
  1189.  * ChangeScreen --
  1190.  *
  1191.  *    This procedure is invoked whenever the current screen changes
  1192.  *    in an application.  It invokes a Tcl procedure named
  1193.  *    "tkScreenChanged", passing it the screen name as argument.
  1194.  *    tkScreenChanged does things like making the tkPriv variable
  1195.  *    point to an array for the current display.
  1196.  *
  1197.  * Results:
  1198.  *    None.
  1199.  *
  1200.  * Side effects:
  1201.  *    Depends on what tkScreenChanged does.  If an error occurs
  1202.  *    them tkError will be invoked.
  1203.  *
  1204.  *----------------------------------------------------------------------
  1205.  */
  1206.  
  1207. static void
  1208. ChangeScreen(interp, dispName, screenIndex)
  1209.     Tcl_Interp *interp;            /* Interpreter in which to invoke
  1210.                      * command. */
  1211.     char *dispName;            /* Name of new display. */
  1212.     int screenIndex;            /* Index of new screen. */
  1213. {
  1214.     Tcl_DString cmd;
  1215.     int code;
  1216.     char screen[30];
  1217.  
  1218.     Tcl_DStringInit(&cmd);
  1219.     Tcl_DStringAppend(&cmd, "tkScreenChanged ", 16);
  1220.     Tcl_DStringAppend(&cmd, dispName, -1);
  1221.     sprintf(screen, ".%d", screenIndex);
  1222.     Tcl_DStringAppend(&cmd, screen, -1);
  1223.     code = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmd));
  1224.     if (code != TCL_OK) {
  1225.     Tcl_AddErrorInfo(interp,
  1226.         "\n    (changing screen in event binding)");
  1227.     Tk_BackgroundError(interp);
  1228.     }
  1229. }
  1230.  
  1231. /*
  1232.  *----------------------------------------------------------------------
  1233.  *
  1234.  * FindSequence --
  1235.  *
  1236.  *    Find the entry in a binding table that corresponds to a
  1237.  *    particular pattern string, and return a pointer to that
  1238.  *    entry.
  1239.  *
  1240.  * Results:
  1241.  *    The return value is normally a pointer to the PatSeq
  1242.  *    in patternTable that corresponds to eventString.  If an error
  1243.  *    was found while parsing eventString, or if "create" is 0 and
  1244.  *    no pattern sequence previously existed, then NULL is returned
  1245.  *    and interp->result contains a message describing the problem.
  1246.  *    If no pattern sequence previously existed for eventString, then
  1247.  *    a new one is created with a NULL command field.  In a successful
  1248.  *    return, *maskPtr is filled in with a mask of the event types
  1249.  *    on which the pattern sequence depends.
  1250.  *
  1251.  * Side effects:
  1252.  *    A new pattern sequence may be created.
  1253.  *
  1254.  *----------------------------------------------------------------------
  1255.  */
  1256.  
  1257. static PatSeq *
  1258. FindSequence(interp, bindPtr, object, eventString, create, maskPtr)
  1259.     Tcl_Interp *interp;        /* Interpreter to use for error
  1260.                  * reporting. */
  1261.     BindingTable *bindPtr;    /* Table to use for lookup. */
  1262.     ClientData object;        /* Token for object(s) with which binding
  1263.                  * is associated. */
  1264.     char *eventString;        /* String description of pattern to
  1265.                  * match on.  See user documentation
  1266.                  * for details. */
  1267.     int create;            /* 0 means don't create the entry if
  1268.                  * it doesn't already exist.   Non-zero
  1269.                  * means create. */
  1270.     unsigned long *maskPtr;    /* *maskPtr is filled in with the event
  1271.                  * types on which this pattern sequence
  1272.                  * depends. */
  1273.  
  1274. {
  1275.     Pattern pats[EVENT_BUFFER_SIZE];
  1276.     int numPats;
  1277.     register char *p;
  1278.     register Pattern *patPtr;
  1279.     register PatSeq *psPtr;
  1280.     register Tcl_HashEntry *hPtr;
  1281. #define FIELD_SIZE 48
  1282.     char field[FIELD_SIZE];
  1283.     int flags, count, new;
  1284.     size_t sequenceSize;
  1285.     unsigned long eventMask;
  1286.     PatternTableKey key;
  1287.  
  1288.     /*
  1289.      *-------------------------------------------------------------
  1290.      * Step 1: parse the pattern string to produce an array
  1291.      * of Patterns.  The array is generated backwards, so
  1292.      * that the lowest-indexed pattern corresponds to the last
  1293.      * event that must occur.
  1294.      *-------------------------------------------------------------
  1295.      */
  1296.  
  1297.     p = eventString;
  1298.     flags = 0;
  1299.     eventMask = 0;
  1300.     for (numPats = 0, patPtr = &pats[EVENT_BUFFER_SIZE-1];
  1301.         numPats < EVENT_BUFFER_SIZE;
  1302.         numPats++, patPtr--) {
  1303.     patPtr->eventType = -1;
  1304.     patPtr->needMods = 0;
  1305.     patPtr->detail = 0;
  1306.     while (isspace(UCHAR(*p))) {
  1307.         p++;
  1308.     }
  1309.     if (*p == '\0') {
  1310.         break;
  1311.     }
  1312.  
  1313.     /*
  1314.      * Handle simple ASCII characters.
  1315.      */
  1316.  
  1317.     if (*p != '<') {
  1318.         char string[2];
  1319.  
  1320.         patPtr->eventType = KeyPress;
  1321.         eventMask |= KeyPressMask;
  1322.         string[0] = *p;
  1323.         string[1] = 0;
  1324.         patPtr->detail = StringToKeysym(string);
  1325.         if (patPtr->detail == NoSymbol) {
  1326.         if (isprint(UCHAR(*p))) {
  1327.             patPtr->detail = *p;
  1328.         } else {
  1329.             sprintf(interp->result,
  1330.                 "bad ASCII character 0x%x", (unsigned char) *p);
  1331.             return NULL;
  1332.         }
  1333.         }
  1334.         p++;
  1335.         continue;
  1336.     }
  1337.  
  1338.     /*
  1339.      * A fancier event description.  Must consist of
  1340.      * 1. open angle bracket.
  1341.      * 2. any number of modifiers, each followed by spaces
  1342.      *    or dashes.
  1343.      * 3. an optional event name.
  1344.      * 4. an option button or keysym name.  Either this or
  1345.      *    item 3 *must* be present;  if both are present
  1346.      *    then they are separated by spaces or dashes.
  1347.      * 5. a close angle bracket.
  1348.      */
  1349.  
  1350.     count = 1;
  1351.     p++;
  1352.     while (1) {
  1353.         register ModInfo *modPtr;
  1354.         p = GetField(p, field, FIELD_SIZE);
  1355.         hPtr = Tcl_FindHashEntry(&modTable, field);
  1356.         if (hPtr == NULL) {
  1357.         break;
  1358.         }
  1359.         modPtr = (ModInfo *) Tcl_GetHashValue(hPtr);
  1360.         patPtr->needMods |= modPtr->mask;
  1361.         if (modPtr->flags & (DOUBLE|TRIPLE)) {
  1362.         flags |= PAT_NEARBY;
  1363.         if (modPtr->flags & DOUBLE) {
  1364.             count = 2;
  1365.         } else {
  1366.             count = 3;
  1367.         }
  1368.         }
  1369.         while ((*p == '-') || isspace(UCHAR(*p))) {
  1370.         p++;
  1371.         }
  1372.     }
  1373.     hPtr = Tcl_FindHashEntry(&eventTable, field);
  1374.     if (hPtr != NULL) {
  1375.         register EventInfo *eiPtr;
  1376.         eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
  1377.         patPtr->eventType = eiPtr->type;
  1378.         eventMask |= eiPtr->eventMask;
  1379.         while ((*p == '-') || isspace(UCHAR(*p))) {
  1380.         p++;
  1381.         }
  1382.         p = GetField(p, field, FIELD_SIZE);
  1383.     }
  1384.     if (*field != '\0') {
  1385.         if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) {
  1386.         if (patPtr->eventType == -1) {
  1387.             patPtr->eventType = ButtonPress;
  1388.             eventMask |= ButtonPressMask;
  1389.         } else if ((patPtr->eventType == KeyPress)
  1390.             || (patPtr->eventType == KeyRelease)) {
  1391.             goto getKeysym;
  1392.         } else if ((patPtr->eventType != ButtonPress)
  1393.             && (patPtr->eventType != ButtonRelease)) {
  1394.             Tcl_AppendResult(interp, "specified button \"", field,
  1395.                 "\" for non-button event", (char *) NULL);
  1396.             return NULL;
  1397.         }
  1398.         patPtr->detail = (*field - '0');
  1399.         } else {
  1400.         getKeysym:
  1401.         patPtr->detail = StringToKeysym(field);
  1402.         if (patPtr->detail == NoSymbol) {
  1403.             Tcl_AppendResult(interp, "bad event type or keysym \"",
  1404.                 field, "\"", (char *) NULL);
  1405.             return NULL;
  1406.         }
  1407.         if (patPtr->eventType == -1) {
  1408.             patPtr->eventType = KeyPress;
  1409.             eventMask |= KeyPressMask;
  1410.         } else if ((patPtr->eventType != KeyPress)
  1411.             && (patPtr->eventType != KeyRelease)) {
  1412.             Tcl_AppendResult(interp, "specified keysym \"", field,
  1413.                 "\" for non-key event", (char *) NULL);
  1414.             return NULL;
  1415.         }
  1416.         }
  1417.     } else if (patPtr->eventType == -1) {
  1418.         interp->result = "no event type or button # or keysym";
  1419.         return NULL;
  1420.     }
  1421.     while ((*p == '-') || isspace(UCHAR(*p))) {
  1422.         p++;
  1423.     }
  1424.     if (*p != '>') {
  1425.         interp->result = "missing \">\" in binding";
  1426.         return NULL;
  1427.     }
  1428.     p++;
  1429.  
  1430.     /*
  1431.      * Replicate events for DOUBLE and TRIPLE.
  1432.      */
  1433.  
  1434.     if ((count > 1) && (numPats < EVENT_BUFFER_SIZE-1)) {
  1435.         patPtr[-1] = patPtr[0];
  1436.         patPtr--;
  1437.         numPats++;
  1438.         if ((count == 3) && (numPats < EVENT_BUFFER_SIZE-1)) {
  1439.         patPtr[-1] = patPtr[0];
  1440.         patPtr--;
  1441.         numPats++;
  1442.         }
  1443.     }
  1444.     }
  1445.  
  1446.     /*
  1447.      *-------------------------------------------------------------
  1448.      * Step 2: find the sequence in the binding table if it exists,
  1449.      * and add a new sequence to the table if it doesn't.
  1450.      *-------------------------------------------------------------
  1451.      */
  1452.  
  1453.     if (numPats == 0) {
  1454.     interp->result = "no events specified in binding";
  1455.     return NULL;
  1456.     }
  1457.     patPtr = &pats[EVENT_BUFFER_SIZE-numPats];
  1458.     key.object = object;
  1459.     key.type = patPtr->eventType;
  1460.     key.detail = patPtr->detail;
  1461.     hPtr = Tcl_CreateHashEntry(&bindPtr->patternTable, (char *) &key, &new);
  1462.     sequenceSize = numPats*sizeof(Pattern);
  1463.     if (!new) {
  1464.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
  1465.         psPtr = psPtr->nextSeqPtr) {
  1466.         if ((numPats == psPtr->numPats)
  1467.             && ((flags & PAT_NEARBY) == (psPtr->flags & PAT_NEARBY))
  1468.             && (memcmp((char *) patPtr, (char *) psPtr->pats,
  1469.             sequenceSize) == 0)) {
  1470.         goto done;
  1471.         }
  1472.     }
  1473.     }
  1474.     if (!create) {
  1475.     if (new) {
  1476.         Tcl_DeleteHashEntry(hPtr);
  1477.     }
  1478.     Tcl_AppendResult(interp, "no binding exists for \"",
  1479.         eventString, "\"", (char *) NULL);
  1480.     return NULL;
  1481.     }
  1482.     psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq)
  1483.         + (numPats-1)*sizeof(Pattern)));
  1484.     psPtr->numPats = numPats;
  1485.     psPtr->command = NULL;
  1486.     psPtr->flags = flags;
  1487.     psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  1488.     psPtr->hPtr = hPtr;
  1489.     Tcl_SetHashValue(hPtr, psPtr);
  1490.  
  1491.     /*
  1492.      * Link the pattern into the list associated with the object.
  1493.      */
  1494.  
  1495.     psPtr->object = object;
  1496.     hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object, &new);
  1497.     if (new) {
  1498.     psPtr->nextObjPtr = NULL;
  1499.     } else {
  1500.     psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  1501.     }
  1502.     Tcl_SetHashValue(hPtr, psPtr);
  1503.  
  1504.     memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);
  1505.  
  1506.     done:
  1507.     *maskPtr = eventMask;
  1508.     return psPtr;
  1509. }
  1510.  
  1511. /*
  1512.  *----------------------------------------------------------------------
  1513.  *
  1514.  * GetField --
  1515.  *
  1516.  *    Used to parse pattern descriptions.  Copies up to
  1517.  *    size characters from p to copy, stopping at end of
  1518.  *    string, space, "-", ">", or whenever size is
  1519.  *    exceeded.
  1520.  *
  1521.  * Results:
  1522.  *    The return value is a pointer to the character just
  1523.  *    after the last one copied (usually "-" or space or
  1524.  *    ">", but could be anything if size was exceeded).
  1525.  *    Also places NULL-terminated string (up to size
  1526.  *    character, including NULL), at copy.
  1527.  *
  1528.  * Side effects:
  1529.  *    None.
  1530.  *
  1531.  *----------------------------------------------------------------------
  1532.  */
  1533.  
  1534. static char *
  1535. GetField(p, copy, size)
  1536.     register char *p;        /* Pointer to part of pattern. */
  1537.     register char *copy;    /* Place to copy field. */
  1538.     int size;            /* Maximum number of characters to
  1539.                  * copy. */
  1540. {
  1541.     while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '>')
  1542.         && (*p != '-') && (size > 1)) {
  1543.     *copy = *p;
  1544.     p++;
  1545.     copy++;
  1546.     size--;
  1547.     }
  1548.     *copy = '\0';
  1549.     return p;
  1550. }
  1551.  
  1552. /*
  1553.  *----------------------------------------------------------------------
  1554.  *
  1555.  * GetKeySym --
  1556.  *
  1557.  *    Given an X KeyPress or KeyRelease event, map the
  1558.  *    keycode in the event into a KeySym.
  1559.  *
  1560.  * Results:
  1561.  *    The return value is the KeySym corresponding to
  1562.  *    eventPtr, or NoSymbol if no matching Keysym could be
  1563.  *    found.
  1564.  *
  1565.  * Side effects:
  1566.  *    In the first call for a given display, keycode-to-
  1567.  *    KeySym maps get loaded.
  1568.  *
  1569.  *----------------------------------------------------------------------
  1570.  */
  1571.  
  1572. static KeySym
  1573. GetKeySym(dispPtr, eventPtr)
  1574.     register TkDisplay *dispPtr;    /* Display in which to
  1575.                      * map keycode. */
  1576.     register XEvent *eventPtr;        /* Description of X event. */
  1577. {
  1578.     KeySym sym;
  1579.     int index;
  1580.  
  1581.     /*
  1582.      * Refresh the mapping information if it's stale
  1583.      */
  1584.  
  1585.     if (dispPtr->bindInfoStale) {
  1586.     InitKeymapInfo(dispPtr);
  1587.     }
  1588.  
  1589.     /*
  1590.      * Figure out which of the four slots in the keymap vector to
  1591.      * use for this key.  Refer to Xlib documentation for more info
  1592.      * on how this computation works.
  1593.      */
  1594.  
  1595.     index = 0;
  1596.     if (eventPtr->xkey.state & dispPtr->modeModMask) {
  1597.     index = 2;
  1598.     }
  1599.     if ((eventPtr->xkey.state & ShiftMask)
  1600.         || ((dispPtr->lockUsage != LU_IGNORE)
  1601.         && (eventPtr->xkey.state & LockMask))) {
  1602.     index += 1;
  1603.     }
  1604.     sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, index);
  1605.  
  1606.     /*
  1607.      * Special handling:  if the key was shifted because of Lock, but
  1608.      * lock is only caps lock, not shift lock, and the shifted keysym
  1609.      * isn't upper-case alphabetic, then switch back to the unshifted
  1610.      * keysym.
  1611.      */
  1612.  
  1613.     if ((index & 1) && !(eventPtr->xkey.state & ShiftMask)
  1614.         && (dispPtr->lockUsage == LU_CAPS)) {
  1615.     if (!(((sym >= XK_A) && (sym <= XK_Z))
  1616.         || ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
  1617.         || ((sym >= XK_Ooblique) && (sym <= XK_Thorn)))) {
  1618.         index &= ~1;
  1619.         sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode,
  1620.             index);
  1621.     }
  1622.     }
  1623.  
  1624.     /*
  1625.      * Another bit of special handling:  if this is a shifted key and there
  1626.      * is no keysym defined, then use the keysym for the unshifted key.
  1627.      */
  1628.  
  1629.     if ((index & 1) && (sym == NoSymbol)) {
  1630.     sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode,
  1631.             index & ~1);
  1632.     }
  1633.     return sym;
  1634. }
  1635.  
  1636. /*
  1637.  *----------------------------------------------------------------------
  1638.  *
  1639.  * MatchPatterns --
  1640.  *
  1641.  *    Given a list of pattern sequences and a list of
  1642.  *    recent events, return a pattern sequence that matches
  1643.  *    the event list.
  1644.  *
  1645.  * Results:
  1646.  *    The return value is NULL if no pattern matches the
  1647.  *    recent events from bindPtr.  If one or more patterns
  1648.  *    matches, then the longest (or most specific) matching
  1649.  *    pattern is returned.
  1650.  *
  1651.  * Side effects:
  1652.  *    None.
  1653.  *
  1654.  *----------------------------------------------------------------------
  1655.  */
  1656.  
  1657. static PatSeq *
  1658. MatchPatterns(dispPtr, bindPtr, psPtr)
  1659.     TkDisplay *dispPtr;        /* Display from which the event came. */
  1660.     BindingTable *bindPtr;    /* Information about binding table, such
  1661.                  * as ring of recent events. */
  1662.     register PatSeq *psPtr;    /* List of pattern sequences. */
  1663. {
  1664.     register PatSeq *bestPtr = NULL;
  1665.  
  1666.     /*
  1667.      * Iterate over all the pattern sequences.
  1668.      */
  1669.  
  1670.     for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
  1671.     register XEvent *eventPtr;
  1672.     register Pattern *patPtr;
  1673.     Window window;
  1674.     int *detailPtr;
  1675.     int patCount, ringCount, flags, state;
  1676.     int modMask;
  1677.  
  1678.     /*
  1679.      * Iterate over all the patterns in a sequence to be
  1680.      * sure that they all match.
  1681.      */
  1682.  
  1683.     eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1684.     detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
  1685.     window = eventPtr->xany.window;
  1686.     patPtr = psPtr->pats;
  1687.     patCount = psPtr->numPats;
  1688.     ringCount = EVENT_BUFFER_SIZE;
  1689.     while (patCount > 0) {
  1690.         if (ringCount <= 0) {
  1691.         goto nextSequence;
  1692.         }
  1693.         if (eventPtr->xany.type != patPtr->eventType) {
  1694.         /*
  1695.          * Most of the event types are considered superfluous
  1696.          * in that they are ignored if they occur in the middle
  1697.          * of a pattern sequence and have mismatching types.  The
  1698.          * only ones that cannot be ignored are ButtonPress and
  1699.          * ButtonRelease events (if the next event in the pattern
  1700.          * is a KeyPress or KeyRelease) and KeyPress and KeyRelease
  1701.          * events (if the next pattern event is a ButtonPress or
  1702.          * ButtonRelease).  Here are some tricky cases to consider:
  1703.          * 1. Double-Button or Double-Key events.
  1704.          * 2. Double-ButtonRelease or Double-KeyRelease events.
  1705.          * 3. The arrival of various events like Enter and Leave
  1706.          *    and FocusIn and GraphicsExpose between two button
  1707.          *    presses or key presses.
  1708.          * 4. Modifier keys like Shift and Control shouldn't
  1709.          *    generate conflicts with button events.
  1710.          */
  1711.  
  1712.         if ((patPtr->eventType == KeyPress)
  1713.             || (patPtr->eventType == KeyRelease)) {
  1714.             if ((eventPtr->xany.type == ButtonPress)
  1715.                 || (eventPtr->xany.type == ButtonRelease)) {
  1716.             goto nextSequence;
  1717.             }
  1718.         } else if ((patPtr->eventType == ButtonPress)
  1719.             || (patPtr->eventType == ButtonRelease)) {
  1720.             if ((eventPtr->xany.type == KeyPress)
  1721.                 || (eventPtr->xany.type == KeyRelease)) {
  1722.             int i;
  1723.  
  1724.             /*
  1725.              * Ignore key events if they are modifier keys.
  1726.              */
  1727.  
  1728.             for (i = 0; i < dispPtr->numModKeyCodes; i++) {
  1729.                 if (dispPtr->modKeyCodes[i]
  1730.                     == eventPtr->xkey.keycode) {
  1731.                 /*
  1732.                  * This key is a modifier key, so ignore it.
  1733.                  */
  1734.                 goto nextEvent;
  1735.                 }
  1736.             }
  1737.             goto nextSequence;
  1738.             }
  1739.         }
  1740.         goto nextEvent;
  1741.         }
  1742.         if (eventPtr->xany.window != window) {
  1743.         goto nextSequence;
  1744.         }
  1745.  
  1746.         /*
  1747.          * Note: it's important for the keysym check to go before
  1748.          * the modifier check, so we can ignore unwanted modifier
  1749.          * keys before choking on the modifier check.
  1750.          */
  1751.  
  1752.         if ((patPtr->detail != 0)
  1753.             && (patPtr->detail != *detailPtr)) {
  1754.         /*
  1755.          * The detail appears not to match.  However, if the event
  1756.          * is a KeyPress for a modifier key then just ignore the
  1757.          * event.  Otherwise event sequences like "aD" never match
  1758.          * because the shift key goes down between the "a" and the
  1759.          * "D".
  1760.          */
  1761.  
  1762.         if (eventPtr->xany.type == KeyPress) {
  1763.             int i;
  1764.  
  1765.             for (i = 0; i < dispPtr->numModKeyCodes; i++) {
  1766.             if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {
  1767.                 goto nextEvent;
  1768.             }
  1769.             }
  1770.         }
  1771.         goto nextSequence;
  1772.         }
  1773.         flags = flagArray[eventPtr->type];
  1774.         if (flags & KEY_BUTTON_MOTION) {
  1775.         state = eventPtr->xkey.state;
  1776.         } else if (flags & CROSSING) {
  1777.         state = eventPtr->xcrossing.state;
  1778.         } else {
  1779.         state = 0;
  1780.         }
  1781.         if (patPtr->needMods != 0) {
  1782.         modMask = patPtr->needMods;
  1783.         if ((modMask & META_MASK) && (dispPtr->metaModMask != 0)) {
  1784.             modMask = (modMask & ~META_MASK) | dispPtr->metaModMask;
  1785.         }
  1786.         if ((modMask & ALT_MASK) && (dispPtr->altModMask != 0)) {
  1787.             modMask = (modMask & ~ALT_MASK) | dispPtr->altModMask;
  1788.         }
  1789.         if ((state & modMask) != modMask) {
  1790.             goto nextSequence;
  1791.         }
  1792.         }
  1793.         if (psPtr->flags & PAT_NEARBY) {
  1794.         register XEvent *firstPtr;
  1795.         int timeDiff;
  1796.  
  1797.         firstPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1798.         timeDiff = (Time) firstPtr->xkey.time - eventPtr->xkey.time;
  1799.         if ((firstPtr->xkey.x_root
  1800.                 < (eventPtr->xkey.x_root - NEARBY_PIXELS))
  1801.             || (firstPtr->xkey.x_root
  1802.                 > (eventPtr->xkey.x_root + NEARBY_PIXELS))
  1803.             || (firstPtr->xkey.y_root
  1804.                 < (eventPtr->xkey.y_root - NEARBY_PIXELS))
  1805.             || (firstPtr->xkey.y_root
  1806.                 > (eventPtr->xkey.y_root + NEARBY_PIXELS))
  1807.             || (timeDiff > NEARBY_MS)) {
  1808.             goto nextSequence;
  1809.         }
  1810.         }
  1811.         patPtr++;
  1812.         patCount--;
  1813.         nextEvent:
  1814.         if (eventPtr == bindPtr->eventRing) {
  1815.         eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
  1816.         detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
  1817.         } else {
  1818.         eventPtr--;
  1819.         detailPtr--;
  1820.         }
  1821.         ringCount--;
  1822.     }
  1823.  
  1824.     /*
  1825.      * This sequence matches.  If we've already got another match,
  1826.      * pick whichever is most specific.  Detail is most important,
  1827.      * then needMods.
  1828.      */
  1829.  
  1830.     if (bestPtr != NULL) {
  1831.         register Pattern *patPtr2;
  1832.         int i;
  1833.  
  1834.         if (psPtr->numPats != bestPtr->numPats) {
  1835.         if (bestPtr->numPats > psPtr->numPats) {
  1836.             goto nextSequence;
  1837.         } else {
  1838.             goto newBest;
  1839.         }
  1840.         }
  1841.         for (i = 0, patPtr = psPtr->pats, patPtr2 = bestPtr->pats;
  1842.             i < psPtr->numPats; i++, patPtr++, patPtr2++) {
  1843.         if (patPtr->detail != patPtr2->detail) {
  1844.             if (patPtr->detail == 0) {
  1845.             goto nextSequence;
  1846.             } else {
  1847.             goto newBest;
  1848.             }
  1849.         }
  1850.         if (patPtr->needMods != patPtr2->needMods) {
  1851.             if ((patPtr->needMods & patPtr2->needMods)
  1852.                 == patPtr->needMods) {
  1853.             goto nextSequence;
  1854.             } else if ((patPtr->needMods & patPtr2->needMods)
  1855.                 == patPtr2->needMods) {
  1856.             goto newBest;
  1857.             }
  1858.         }
  1859.         }
  1860.         goto nextSequence;    /* Tie goes to newest pattern. */
  1861.     }
  1862.     newBest:
  1863.     bestPtr = psPtr;
  1864.  
  1865.     nextSequence: continue;
  1866.     }
  1867.     return bestPtr;
  1868. }
  1869.  
  1870. /*
  1871.  *--------------------------------------------------------------
  1872.  *
  1873.  * ExpandPercents --
  1874.  *
  1875.  *    Given a command and an event, produce a new command
  1876.  *    by replacing % constructs in the original command
  1877.  *    with information from the X event.
  1878.  *
  1879.  * Results:
  1880.  *    The new expanded command is appended to the dynamic string
  1881.  *    given by dsPtr.
  1882.  *
  1883.  * Side effects:
  1884.  *    None.
  1885.  *
  1886.  *--------------------------------------------------------------
  1887.  */
  1888.  
  1889. static void
  1890. ExpandPercents(winPtr, before, eventPtr, keySym, dsPtr)
  1891.     TkWindow *winPtr;        /* Window where event occurred:  needed to
  1892.                  * get input context. */
  1893.     register char *before;    /* Command containing percent
  1894.                  * expressions to be replaced. */
  1895.     register XEvent *eventPtr;    /* X event containing information
  1896.                  * to be used in % replacements. */
  1897.     KeySym keySym;        /* KeySym: only relevant for
  1898.                  * KeyPress and KeyRelease events). */
  1899.     Tcl_DString *dsPtr;        /* Dynamic string in which to append
  1900.                  * new command. */
  1901. {
  1902.     int spaceNeeded, cvtFlags;    /* Used to substitute string as proper Tcl
  1903.                  * list element. */
  1904.     int number, flags, length;
  1905. #define NUM_SIZE 40
  1906.     register char *string;
  1907.     char numStorage[NUM_SIZE+1];
  1908.  
  1909.     if (eventPtr->type < LASTEvent) {
  1910.     flags = flagArray[eventPtr->type];
  1911.     } else {
  1912.     flags = 0;
  1913.     }
  1914.     while (1) {
  1915.     /*
  1916.      * Find everything up to the next % character and append it
  1917.      * to the result string.
  1918.      */
  1919.  
  1920.     for (string = before; (*string != 0) && (*string != '%'); string++) {
  1921.         /* Empty loop body. */
  1922.     }
  1923.     if (string != before) {
  1924.         Tcl_DStringAppend(dsPtr, before, string-before);
  1925.         before = string;
  1926.     }
  1927.     if (*before == 0) {
  1928.         break;
  1929.     }
  1930.  
  1931.     /*
  1932.      * There's a percent sequence here.  Process it.
  1933.      */
  1934.  
  1935.     number = 0;
  1936.     string = "??";
  1937.     switch (before[1]) {
  1938.         case '#':
  1939.         number = eventPtr->xany.serial;
  1940.         goto doNumber;
  1941.         case 'a':
  1942.         sprintf(numStorage, "0x%x", (int) eventPtr->xconfigure.above);
  1943.         string = numStorage;
  1944.         goto doString;
  1945.         case 'b':
  1946.         number = eventPtr->xbutton.button;
  1947.         goto doNumber;
  1948.         case 'c':
  1949.         if (flags & EXPOSE) {
  1950.             number = eventPtr->xexpose.count;
  1951.         } else if (flags & MAPPING) {
  1952.             number = eventPtr->xmapping.count;
  1953.         }
  1954.         goto doNumber;
  1955.         case 'd':
  1956.         if (flags & (CROSSING|FOCUS)) {
  1957.             if (flags & FOCUS) {
  1958.             number = eventPtr->xfocus.detail;
  1959.             } else {
  1960.             number = eventPtr->xcrossing.detail;
  1961.             }
  1962.             switch (number) {
  1963.             case NotifyAncestor:
  1964.                 string = "NotifyAncestor";
  1965.                 break;
  1966.             case NotifyVirtual:
  1967.                 string = "NotifyVirtual";
  1968.                 break;
  1969.             case NotifyInferior:
  1970.                 string = "NotifyInferior";
  1971.                 break;
  1972.             case NotifyNonlinear:
  1973.                 string = "NotifyNonlinear";
  1974.                 break;
  1975.             case NotifyNonlinearVirtual:
  1976.                 string = "NotifyNonlinearVirtual";
  1977.                 break;
  1978.             case NotifyPointer:
  1979.                 string = "NotifyPointer";
  1980.                 break;
  1981.             case NotifyPointerRoot:
  1982.                 string = "NotifyPointerRoot";
  1983.                 break;
  1984.             case NotifyDetailNone:
  1985.                 string = "NotifyDetailNone";
  1986.                 break;
  1987.             }
  1988.         } else if (flags & CONFIG_REQ) {
  1989.             switch (eventPtr->xconfigurerequest.detail) {
  1990.             case Above:
  1991.                 string = "Above";
  1992.                 break;
  1993.             case Below:
  1994.                 string = "Below";
  1995.                 break;
  1996.             case TopIf:
  1997.                 string = "TopIf";
  1998.                 break;
  1999.             case BottomIf:
  2000.                 string = "BottomIf";
  2001.                 break;
  2002.             case Opposite:
  2003.                 string = "Opposite";
  2004.                 break;
  2005.             }
  2006.         }
  2007.         goto doString;
  2008.         case 'f':
  2009.         number = eventPtr->xcrossing.focus;
  2010.         goto doNumber;
  2011.         case 'h':
  2012.         if (flags & EXPOSE) {
  2013.             number = eventPtr->xexpose.height;
  2014.         } else if (flags & (CONFIG|CONFIG_REQ)) {
  2015.             number = eventPtr->xconfigure.height;
  2016.         } else if (flags & RESIZE_REQ) {
  2017.             number = eventPtr->xresizerequest.height;
  2018.         }
  2019.         goto doNumber;
  2020.         case 'k':
  2021.         number = eventPtr->xkey.keycode;
  2022.         goto doNumber;
  2023.         case 'm':
  2024.         if (flags & CROSSING) {
  2025.             number = eventPtr->xcrossing.mode;
  2026.         } else if (flags & FOCUS) {
  2027.             number = eventPtr->xfocus.mode;
  2028.         }
  2029.         switch (number) {
  2030.             case NotifyNormal:
  2031.             string = "NotifyNormal";
  2032.             break;
  2033.             case NotifyGrab:
  2034.             string = "NotifyGrab";
  2035.             break;
  2036.             case NotifyUngrab:
  2037.             string = "NotifyUngrab";
  2038.             break;
  2039.             case NotifyWhileGrabbed:
  2040.             string = "NotifyWhileGrabbed";
  2041.             break;
  2042.         }
  2043.         goto doString;
  2044.         case 'o':
  2045.         if (flags & CREATE) {
  2046.             number = eventPtr->xcreatewindow.override_redirect;
  2047.         } else if (flags & MAP) {
  2048.             number = eventPtr->xmap.override_redirect;
  2049.         } else if (flags & REPARENT) {
  2050.             number = eventPtr->xreparent.override_redirect;
  2051.         } else if (flags & CONFIG) {
  2052.             number = eventPtr->xconfigure.override_redirect;
  2053.         }
  2054.         goto doNumber;
  2055.         case 'p':
  2056.         switch (eventPtr->xcirculate.place) {
  2057.             case PlaceOnTop:
  2058.             string = "PlaceOnTop";
  2059.             break;
  2060.             case PlaceOnBottom:
  2061.             string = "PlaceOnBottom";
  2062.             break;
  2063.         }
  2064.         goto doString;
  2065.         case 's':
  2066.         if (flags & KEY_BUTTON_MOTION) {
  2067.             number = eventPtr->xkey.state;
  2068.         } else if (flags & CROSSING) {
  2069.             number = eventPtr->xcrossing.state;
  2070.         } else if (flags & VISIBILITY) {
  2071.             switch (eventPtr->xvisibility.state) {
  2072.             case VisibilityUnobscured:
  2073.                 string = "VisibilityUnobscured";
  2074.                 break;
  2075.             case VisibilityPartiallyObscured:
  2076.                 string = "VisibilityPartiallyObscured";
  2077.                 break;
  2078.             case VisibilityFullyObscured:
  2079.                 string = "VisibilityFullyObscured";
  2080.                 break;
  2081.             }
  2082.             goto doString;
  2083.         }
  2084.         goto doNumber;
  2085.         case 't':
  2086.         if (flags & (KEY_BUTTON_MOTION|PROP|SEL_CLEAR)) {
  2087.             number = (int) eventPtr->xkey.time;
  2088.         } else if (flags & SEL_REQ) {
  2089.             number = (int) eventPtr->xselectionrequest.time;
  2090.         } else if (flags & SEL_NOTIFY) {
  2091.             number = (int) eventPtr->xselection.time;
  2092.         } else if (flags & CROSSING) {
  2093.             number = (int) eventPtr->xcrossing.time;
  2094.         }
  2095.         goto doNumber;
  2096.         case 'v':
  2097.         number = eventPtr->xconfigurerequest.value_mask;
  2098.         goto doNumber;
  2099.         case 'w':
  2100.         if (flags & EXPOSE) {
  2101.             number = eventPtr->xexpose.width;
  2102.         } else if (flags & (CONFIG|CONFIG_REQ)) {
  2103.             number = eventPtr->xconfigure.width;
  2104.         } else if (flags & RESIZE_REQ) {
  2105.             number = eventPtr->xresizerequest.width;
  2106.         }
  2107.         goto doNumber;
  2108.         case 'x':
  2109.         if (flags & KEY_BUTTON_MOTION) {
  2110.             number = eventPtr->xkey.x;
  2111.         } else if (flags & EXPOSE) {
  2112.             number = eventPtr->xexpose.x;
  2113.         } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
  2114.             number = eventPtr->xcreatewindow.x;
  2115.         } else if (flags & REPARENT) {
  2116.             number = eventPtr->xreparent.x;
  2117.         } else if (flags & CROSSING) {
  2118.             number = eventPtr->xcrossing.x;
  2119.         }
  2120.         goto doNumber;
  2121.         case 'y':
  2122.         if (flags & KEY_BUTTON_MOTION) {
  2123.             number = eventPtr->xkey.y;
  2124.         } else if (flags & EXPOSE) {
  2125.             number = eventPtr->xexpose.y;
  2126.         } else if (flags & (CREATE|CONFIG|GRAVITY|CONFIG_REQ)) {
  2127.             number = eventPtr->xcreatewindow.y;
  2128.         } else if (flags & REPARENT) {
  2129.             number = eventPtr->xreparent.y;
  2130.         } else if (flags & CROSSING) {
  2131.             number = eventPtr->xcrossing.y;
  2132.  
  2133.         }
  2134.         goto doNumber;
  2135.         case 'A':
  2136.         if ((eventPtr->type == KeyPress)
  2137.             || (eventPtr->type == KeyRelease)) {
  2138.             int numChars;
  2139.  
  2140.             /*
  2141.              * If we're using input methods and this is a keypress
  2142.              * event, invoke XmbLookupString.  Otherwise just use
  2143.              * the older XLookupString.
  2144.              */
  2145.  
  2146. #ifdef TK_USE_INPUT_METHODS
  2147.             Status status;
  2148.             if ((winPtr->inputContext != NULL)
  2149.                 && (eventPtr->type == KeyPress)) {
  2150.                         numChars = XmbLookupString(winPtr->inputContext,
  2151.                                 &eventPtr->xkey, numStorage, NUM_SIZE,
  2152.                                 (KeySym *) NULL, &status);
  2153.             if ((status != XLookupChars)
  2154.                 && (status != XLookupBoth)) {
  2155.                 numChars = 0;
  2156.             }
  2157.                     } else {
  2158.                         numChars = XLookupString(&eventPtr->xkey, numStorage,
  2159.                                 NUM_SIZE, (KeySym *) NULL,
  2160.                                 (XComposeStatus *) NULL);
  2161.             }
  2162. #else /* TK_USE_INPUT_METHODS */
  2163.             numChars = XLookupString(&eventPtr->xkey, numStorage,
  2164.                 NUM_SIZE, (KeySym *) NULL,
  2165.                 (XComposeStatus *) NULL);
  2166. #endif /* TK_USE_INPUT_METHODS */
  2167.             numStorage[numChars] = '\0';
  2168.             string = numStorage;
  2169.         }
  2170.         goto doString;
  2171.         case 'B':
  2172.         number = eventPtr->xcreatewindow.border_width;
  2173.         goto doNumber;
  2174.         case 'E':
  2175.         number = (int) eventPtr->xany.send_event;
  2176.         goto doNumber;
  2177.         case 'K':
  2178.         if ((eventPtr->type == KeyPress)
  2179.             || (eventPtr->type == KeyRelease)) {
  2180.             char *name;
  2181.  
  2182.             name = XKeysymToString(keySym);
  2183.             if (name != NULL) {
  2184.             string = name;
  2185.             }
  2186.         }
  2187.         goto doString;
  2188.         case 'N':
  2189.         number = (int) keySym;
  2190.         goto doNumber;
  2191.         case 'R':
  2192.         number = (int) eventPtr->xkey.root;
  2193.         goto doNumber;
  2194.         case 'S':
  2195.         sprintf(numStorage, "0x%x", (int) eventPtr->xkey.subwindow);
  2196.         string = numStorage;
  2197.         goto doString;
  2198.         case 'T':
  2199.         number = eventPtr->type;
  2200.         goto doNumber;
  2201.         case 'W': {
  2202.         Tk_Window tkwin;
  2203.  
  2204.         tkwin = Tk_IdToWindow(eventPtr->xany.display,
  2205.             eventPtr->xany.window);
  2206.         if (tkwin != NULL) {
  2207.             string = Tk_PathName(tkwin);
  2208.         } else {
  2209.             string = "??";
  2210.         }
  2211.         goto doString;
  2212.         }
  2213.         case 'X': {
  2214.         Tk_Window tkwin;
  2215.         int x, y;
  2216.         int width, height;
  2217.  
  2218.         number = eventPtr->xkey.x_root;
  2219.         tkwin = Tk_IdToWindow(eventPtr->xany.display,
  2220.             eventPtr->xany.window);
  2221.         if (tkwin != NULL) {
  2222.             Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
  2223.             number -= x;
  2224.         }
  2225.         goto doNumber;
  2226.         }
  2227.         case 'Y': {
  2228.         Tk_Window tkwin;
  2229.         int x, y;
  2230.         int width, height;
  2231.  
  2232.         number = eventPtr->xkey.y_root;
  2233.         tkwin = Tk_IdToWindow(eventPtr->xany.display,
  2234.             eventPtr->xany.window);
  2235.         if (tkwin != NULL) {
  2236.             Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
  2237.             number -= y;
  2238.         }
  2239.         goto doNumber;
  2240.         }
  2241.         default:
  2242.         numStorage[0] = before[1];
  2243.         numStorage[1] = '\0';
  2244.         string = numStorage;
  2245.         goto doString;
  2246.     }
  2247.  
  2248.     doNumber:
  2249.     sprintf(numStorage, "%d", number);
  2250.     string = numStorage;
  2251.  
  2252.     doString:
  2253.     spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
  2254.     length = Tcl_DStringLength(dsPtr);
  2255.     Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
  2256.     spaceNeeded = Tcl_ConvertElement(string,
  2257.         Tcl_DStringValue(dsPtr) + length,
  2258.         cvtFlags | TCL_DONT_USE_BRACES);
  2259.     Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
  2260.     before += 2;
  2261.     }
  2262. }
  2263.  
  2264. /*
  2265.  *----------------------------------------------------------------------
  2266.  *
  2267.  * TkCopyAndGlobalEval --
  2268.  *
  2269.  *    This procedure makes a copy of a script then calls Tcl_GlobalEval
  2270.  *    to evaluate it.  It's used in situations where the execution of
  2271.  *    a command may cause the original command string to be reallocated.
  2272.  *
  2273.  * Results:
  2274.  *    Returns the result of evaluating script, including both a standard
  2275.  *    Tcl completion code and a string in interp->result.
  2276.  *
  2277.  * Side effects:
  2278.  *    None.
  2279.  *
  2280.  *----------------------------------------------------------------------
  2281.  */
  2282.  
  2283. int
  2284. TkCopyAndGlobalEval(interp, script)
  2285.     Tcl_Interp *interp;            /* Interpreter in which to evaluate
  2286.                      * script. */
  2287.     char *script;            /* Script to evaluate. */
  2288. {
  2289.     Tcl_DString buffer;
  2290.     int code;
  2291.  
  2292.     Tcl_DStringInit(&buffer);
  2293.     Tcl_DStringAppend(&buffer, script, -1);
  2294.     code = Tcl_GlobalEval(interp, Tcl_DStringValue(&buffer));
  2295.     Tcl_DStringFree(&buffer);
  2296.     return code;
  2297. }
  2298.  
  2299. /*
  2300.  *--------------------------------------------------------------
  2301.  *
  2302.  * InitKeymapInfo --
  2303.  *
  2304.  *    This procedure is invoked to scan keymap information
  2305.  *    to recompute stuff that's important for binding, such
  2306.  *    as the modifier key (if any) that corresponds to "mode
  2307.  *    switch".
  2308.  *
  2309.  * Results:
  2310.  *    None.
  2311.  *
  2312.  * Side effects:
  2313.  *    Keymap-related information in dispPtr is updated.
  2314.  *
  2315.  *--------------------------------------------------------------
  2316.  */
  2317.  
  2318. static void
  2319. InitKeymapInfo(dispPtr)
  2320.     TkDisplay *dispPtr;        /* Display for which to recompute keymap
  2321.                  * information. */
  2322. {
  2323.     XModifierKeymap *modMapPtr;
  2324.     register KeyCode *codePtr;
  2325.     KeySym keysym;
  2326.     int count, i, j, max, arraySize;
  2327. #define KEYCODE_ARRAY_SIZE 20
  2328.  
  2329.     dispPtr->bindInfoStale = 0;
  2330.     modMapPtr = XGetModifierMapping(dispPtr->display);
  2331.  
  2332.     /*
  2333.      * Check the keycodes associated with the Lock modifier.  If
  2334.      * any of them is associated with the XK_Shift_Lock modifier,
  2335.      * then Lock has to be interpreted as Shift Lock, not Caps Lock.
  2336.      */
  2337.  
  2338.     dispPtr->lockUsage = LU_IGNORE;
  2339.     codePtr = modMapPtr->modifiermap + modMapPtr->max_keypermod*LockMapIndex;
  2340.     for (count = modMapPtr->max_keypermod; count > 0; count--, codePtr++) {
  2341.     if (*codePtr == 0) {
  2342.         continue;
  2343.     }
  2344.     keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);
  2345.     if (keysym == XK_Shift_Lock) {
  2346.         dispPtr->lockUsage = LU_SHIFT;
  2347.         break;
  2348.     }
  2349.     if (keysym == XK_Caps_Lock) {
  2350.         dispPtr->lockUsage = LU_CAPS;
  2351.         break;
  2352.     }
  2353.     }
  2354.  
  2355.     /*
  2356.      * Look through the keycodes associated with modifiers to see if
  2357.      * the the "mode switch", "meta", or "alt" keysyms are associated
  2358.      * with any modifiers.  If so, remember their modifier mask bits.
  2359.      */
  2360.  
  2361.     dispPtr->modeModMask = 0;
  2362.     dispPtr->metaModMask = 0;
  2363.     dispPtr->altModMask = 0;
  2364.     codePtr = modMapPtr->modifiermap;
  2365.     max = 8*modMapPtr->max_keypermod;
  2366.     for (i = 0; i < max; i++, codePtr++) {
  2367.     if (*codePtr == 0) {
  2368.         continue;
  2369.     }
  2370.     keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);
  2371.     if (keysym == XK_Mode_switch) {
  2372.         dispPtr->modeModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
  2373.     }
  2374.     if ((keysym == XK_Meta_L) || (keysym == XK_Meta_R)) {
  2375.         dispPtr->metaModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
  2376.     }
  2377.     if ((keysym == XK_Alt_L) || (keysym == XK_Alt_R)) {
  2378.         dispPtr->altModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
  2379.     }
  2380.     }
  2381.  
  2382.     /*
  2383.      * Create an array of the keycodes for all modifier keys.
  2384.      */
  2385.  
  2386.     if (dispPtr->modKeyCodes != NULL) {
  2387.     ckfree((char *) dispPtr->modKeyCodes);
  2388.     }
  2389.     dispPtr->numModKeyCodes = 0;
  2390.     arraySize = KEYCODE_ARRAY_SIZE;
  2391.     dispPtr->modKeyCodes = (KeyCode *) ckalloc((unsigned)
  2392.         (KEYCODE_ARRAY_SIZE * sizeof(KeyCode)));
  2393.     for (i = 0, codePtr = modMapPtr->modifiermap; i < max; i++, codePtr++) {
  2394.     if (*codePtr == 0) {
  2395.         continue;
  2396.     }
  2397.  
  2398.     /*
  2399.      * Make sure that the keycode isn't already in the array.
  2400.      */
  2401.  
  2402.     for (j = 0; j < dispPtr->numModKeyCodes; j++) {
  2403.         if (dispPtr->modKeyCodes[j] == *codePtr) {
  2404.         goto nextModCode;
  2405.         }
  2406.     }
  2407.     if (dispPtr->numModKeyCodes >= arraySize) {
  2408.         KeyCode *new;
  2409.  
  2410.         /*
  2411.          * Ran out of space in the array;  grow it.
  2412.          */
  2413.  
  2414.         arraySize *= 2;
  2415.         new = (KeyCode *) ckalloc((unsigned)
  2416.             (arraySize * sizeof(KeyCode)));
  2417.         memcpy((VOID *) new, (VOID *) dispPtr->modKeyCodes,
  2418.             (dispPtr->numModKeyCodes * sizeof(KeyCode)));
  2419.         ckfree((char *) dispPtr->modKeyCodes);
  2420.         dispPtr->modKeyCodes = new;
  2421.     }
  2422.     dispPtr->modKeyCodes[dispPtr->numModKeyCodes] = *codePtr;
  2423.     dispPtr->numModKeyCodes++;
  2424.     nextModCode: continue;
  2425.     }
  2426.     XFreeModifiermap(modMapPtr);
  2427. }
  2428.  
  2429. /*
  2430.  *----------------------------------------------------------------------
  2431.  *
  2432.  * StringToKeysym --
  2433.  *
  2434.  *    This procedure finds the keysym associated with a given keysym
  2435.  *    name.
  2436.  *
  2437.  * Results:
  2438.  *    The return value is the keysym that corresponds to name, or
  2439.  *    NoSymbol if there is no such keysym.
  2440.  *
  2441.  * Side effects:
  2442.  *    None.
  2443.  *
  2444.  *----------------------------------------------------------------------
  2445.  */
  2446.  
  2447. static KeySym
  2448. StringToKeysym(name)
  2449.     char *name;            /* Name of a keysym. */
  2450. {
  2451. #ifdef REDO_KEYSYM_LOOKUP
  2452.     Tcl_HashEntry *hPtr;
  2453.  
  2454.     hPtr = Tcl_FindHashEntry(&keySymTable, name);
  2455.     if (hPtr != NULL) {
  2456.     return (KeySym) Tcl_GetHashValue(hPtr);
  2457.     }
  2458. #endif /* REDO_KEYSYM_LOOKUP */
  2459.     return XStringToKeysym(name);
  2460. }
  2461.